package me.flyray.bsin.server.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import me.flyray.bsin.blockchain.core.BsinBlockChainEngine;
import me.flyray.bsin.blockchain.dto.ChainWalletDto;
import me.flyray.bsin.blockchain.connection.BsinBlockChainEngineFactory;
import me.flyray.bsin.context.BsinServiceContext;
import me.flyray.bsin.domain.entity.*;
import me.flyray.bsin.domain.enums.WalletStatus;
import me.flyray.bsin.domain.enums.WalletTag;
import me.flyray.bsin.blockchain.enums.WalletType;
import me.flyray.bsin.domain.request.WalletDTO;
import me.flyray.bsin.domain.response.WalletVO;
import me.flyray.bsin.exception.BusinessException;
import me.flyray.bsin.facade.engine.WalletServiceEngine;
import me.flyray.bsin.infrastructure.biz.WalletAccountBiz;
import me.flyray.bsin.infrastructure.mapper.BizRoleTypeChainCoinMapper;
import me.flyray.bsin.infrastructure.mapper.ChainCoinMapper;
import me.flyray.bsin.infrastructure.mapper.WalletAccountMapper;
import me.flyray.bsin.infrastructure.mapper.WalletMapper;
import me.flyray.bsin.mybatis.utils.Pagination;
import me.flyray.bsin.security.contex.LoginInfoContextHelper;
import me.flyray.bsin.security.domain.LoginUser;
import me.flyray.bsin.security.enums.BizRoleType;
import me.flyray.bsin.utils.BsinSnowflake;
import me.flyray.bsin.validate.QueryGroup;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.validation.annotation.Validated;
import org.apache.shenyu.client.apache.dubbo.annotation.ShenyuDubboService;
import me.flyray.bsin.facade.service.PlatformService;
import org.apache.shenyu.client.apidocs.annotations.ApiDoc;
import org.apache.shenyu.client.apidocs.annotations.ApiModule;
import org.apache.shenyu.client.dubbo.common.annotation.ShenyuDubboClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @author bolei
 * @date 2023/6/26 15:21
 * @desc
 */

@Slf4j
@ShenyuDubboService(path = "/wallet", timeout = 30000)
@ApiModule(value = "wallet")
public class WalletServiceImpl implements WalletServiceEngine {

  @Autowired private BsinBlockChainEngineFactory bsinBlockChainEngineFactory;

  @Autowired
  private WalletMapper walletMapper;
  @Autowired
  private WalletAccountMapper walletAccountMapper;
  @Autowired
  private WalletAccountBiz walletAccountBiz;
  @Autowired
  private BizRoleTypeChainCoinMapper customerChainCoinMapper;
  @Autowired
  private ChainCoinMapper chainCoinMapper;

  @DubboReference(version = "${dubbo.provider.version}")
  private PlatformService platformService;

  /**
   * 1、生成普通钱包账户
   * 2、生成6551智能合约钱包账户
   *
   * @param requestMap
   * @return
   * @throws Exception
   */
  @Override
  @ApiDoc(desc = "createWalletAccount")
  @ShenyuDubboClient("/createWalletAccount")
  public Map<String, Object> createWalletAccount(Map<String, Object> requestMap) throws Exception {
    LoginUser loginUser = LoginInfoContextHelper.getLoginUser();
    ChainWalletDto chainWalletDto =
        BsinServiceContext.getReqBodyDto(ChainWalletDto.class, requestMap);
    BsinBlockChainEngine bsinBlockChainEngine =
        bsinBlockChainEngineFactory.getBsinBlockChainEngineInstance(chainWalletDto.getChainIdentifier());
    Map<String, Object> wallet =
        bsinBlockChainEngine.createWallet(
            chainWalletDto.getPassword(), chainWalletDto.getChainEnv());
    Map<String, Object> requestWalletMap = new HashMap<>();
    requestWalletMap.put("customerNo", loginUser.getCustomerNo());
    requestWalletMap.put("walletAddress", wallet.get("address"));
    requestWalletMap.put("walletPrivateKey", wallet.get("privateKey"));
    return requestWalletMap;
  }

  /**
   * 1、导入钱包
   * @param walletReq
   * @return
   * @throws Exception
   */
  @Override
  @ApiDoc(desc = "importWallet")
  @ShenyuDubboClient("/importWallet")
  public Wallet importWallet(Wallet walletReq) throws Exception {
    log.info("请求WalletService.importWallet,参数:{}", walletReq);
    LoginUser loginUser = LoginInfoContextHelper.getLoginUser();
    Wallet wallet = new Wallet();
    BeanUtils.copyProperties(walletReq, wallet);

    // 2、创建钱包
    String serialNo = BsinSnowflake.getId();
    wallet.setSerialNo(serialNo);
    wallet.setStatus(WalletStatus.NORMAL.getCode()); // 正常
    wallet.setWalletTag(WalletTag.DEPOSIT.getCode());
    wallet.setCreateBy(loginUser.getUserId());
    wallet.setCreateTime(new Date());
    
    wallet.setTenantId(StringUtils.defaultIfBlank(wallet.getTenantId(), loginUser.getTenantId()));
    wallet.setBizRoleTypeNo(StringUtils.defaultIfBlank(wallet.getBizRoleTypeNo(), loginUser.getMerchantNo()));
    
    walletMapper.insert(wallet);
    return wallet;
  }

  @Override
  @ApiDoc(desc = "createMPCWallet")
  @ShenyuDubboClient("/createMPCWallet")
  @Transactional(rollbackFor = Exception.class)
  public Wallet createMPCWallet(Wallet walletReq) {
    log.info("开始创建MPC钱包，请求参数: {}", walletReq);
    LoginUser loginUser = LoginInfoContextHelper.getLoginUser();
    log.info("当前登录用户信息 - 用户名: {}, 租户ID: {}, 商户编号: {}", 
        loginUser.getUsername(), loginUser.getTenantId(), loginUser.getMerchantNo());
    
    Wallet wallet = new Wallet();
    BeanUtils.copyProperties(walletReq, wallet);
    
    // 设置租户和角色信息
    wallet.setTenantId(StringUtils.defaultIfBlank(wallet.getTenantId(), loginUser.getTenantId()));
    wallet.setBizRoleTypeNo(StringUtils.defaultIfBlank(wallet.getBizRoleTypeNo(), loginUser.getMerchantNo()));
    
    // 1、如果是默认钱包，检查是否已存在，存在则直接返回
    if(WalletType.DEFAULT.getCode().equals(wallet.getType()) && WalletType.MPC_DEFAULT.getCode().equals(wallet.getType())){
      Wallet existingWallet = getDefaultWalletIfExists(wallet.getTenantId(), wallet.getBizRoleType(), wallet.getBizRoleTypeNo());
      if(existingWallet != null){
        log.info("默认钱包已存在，直接返回，钱包编号: {}", existingWallet.getSerialNo());
        return existingWallet;
      }
    }

    // 2、创建钱包
    String serialNo = BsinSnowflake.getId();
    wallet.setSerialNo(serialNo);
    wallet.setWalletName(loginUser.getUsername() + WalletTag.DEPOSIT.getDesc() + "钱包");
    wallet.setStatus(WalletStatus.NORMAL.getCode());
    wallet.setWalletTag(WalletTag.DEPOSIT.getCode());
    wallet.setCreateBy(loginUser.getBizRoleTypeNo());
    wallet.setCreateTime(new Date());
    
    log.info("MPC钱包基础信息已设置 - 钱包编号: {}, 钱包名称: {}, 钱包类型: {}, 状态: {}", 
        serialNo, wallet.getWalletName(), wallet.getType(), wallet.getStatus());
    walletMapper.insert(wallet);
    log.info("MPC钱包记录已插入数据库，钱包编号: {}", serialNo);

    List<ChainCoin> chainCoinList = new ArrayList<>();
    // 默认EVM钱包
    if(WalletType.DEFAULT.getCode().equals(wallet.getType())){
      log.info("创建默认MPC EVM钱包，租户ID: {}, 角色类型: {}, 角色编号: {}", 
          wallet.getTenantId(), wallet.getBizRoleType(), wallet.getBizRoleTypeNo());
      
      BizRoleTypeChainCoin customerChainCoin = new BizRoleTypeChainCoin();
      customerChainCoin.setTenantId(wallet.getTenantId());
      customerChainCoin.setBizRoleType(wallet.getBizRoleType());
      customerChainCoin.setBizRoleTypeNo(wallet.getBizRoleTypeNo());
      if(!BizRoleType.MERCHANT.getCode().equals(wallet.getBizRoleType())){
        customerChainCoin.setCreateRoleAccountFlag(1);
      }else {
        customerChainCoin.setCreateCustomerAccountFlag(0);
      }
      chainCoinList = customerChainCoinMapper.selectChainCoinList(customerChainCoin);
      log.info("查询到支持的币种数量: {}", chainCoinList != null ? chainCoinList.size() : 0);
    }else if( WalletType.CUSTOMIZE.getCode().equals(wallet.getType())){
      log.info("创建自定义MPC钱包，环境: {}", wallet.getEnv());
      // 自定义钱包
      if(wallet.getEnv().equals("EVM")){
        // TODO EVM 对应的本币
        log.warn("自定义MPC EVM钱包创建逻辑待实现");
      }
    }

    // 3、 根据支持的币种创建钱包地址，并创建钱包账户
    if(chainCoinList != null && !chainCoinList.isEmpty()){
      log.info("开始为MPC钱包 {} 创建 {} 个币种账户", serialNo, chainCoinList.size());
      int accountCount = 0;
      for(ChainCoin chainCoin : chainCoinList){
        try {
          walletAccountBiz.createMpcWalletAccount(wallet, chainCoin.getSerialNo());
          accountCount++;
          log.debug("成功创建MPC钱包账户，币种编号: {}, 币种名称: {}", 
              chainCoin.getSerialNo(), chainCoin.getChainCoinName());
        } catch (Exception e) {
          log.error("创建MPC钱包账户失败，币种编号: {}, 错误信息: {}", 
              chainCoin.getSerialNo(), e.getMessage(), e);
          throw e;
        }
      }
      log.info("MPC钱包账户创建完成，成功创建 {}/{} 个账户", accountCount, chainCoinList.size());
    } else {
      log.warn("未找到支持的币种，跳过MPC钱包账户创建");
    }
    
    log.info("MPC钱包创建成功，钱包编号: {}, 钱包名称: {}", wallet.getSerialNo(), wallet.getWalletName());
    return wallet;
  }


  @Override
  @ApiDoc(desc = "createWallet")
  @ShenyuDubboClient("/createWallet")
  @Transactional(rollbackFor = Exception.class)
  public Wallet createWallet(Wallet walletReq) throws Exception {
    log.info("开始创建钱包，请求参数: {}", walletReq);
    LoginUser loginUser = LoginInfoContextHelper.getLoginUser();
    log.info("当前登录用户信息 - 用户名: {}, 租户ID: {}, 角色类型: {}, 角色编号: {}", 
        loginUser.getUsername(), loginUser.getTenantId(), 
        loginUser.getBizRoleType(), loginUser.getBizRoleTypeNo());
    
    Wallet wallet = new Wallet();
    BeanUtils.copyProperties(walletReq, wallet);
    
    // 设置租户和角色信息
    wallet.setTenantId(StringUtils.defaultIfBlank(wallet.getTenantId(), loginUser.getTenantId()));
    wallet.setBizRoleTypeNo(StringUtils.defaultIfBlank(wallet.getBizRoleTypeNo(), loginUser.getBizRoleTypeNo()));
    wallet.setBizRoleType(StringUtils.defaultIfBlank(wallet.getBizRoleType(), loginUser.getBizRoleType()));
    
    // 1、如果是默认钱包，检查是否已存在，存在则直接返回
    if(WalletType.DEFAULT.getCode().equals(wallet.getType()) && WalletType.MPC_DEFAULT.getCode().equals(wallet.getType())){
      Wallet existingWallet = getDefaultWalletIfExists(wallet.getTenantId(), wallet.getBizRoleType(), wallet.getBizRoleTypeNo());
      if(existingWallet != null){
        log.info("默认钱包已存在，直接返回，钱包编号: {}", existingWallet.getSerialNo());
        return existingWallet;
      }
    }

    // 2、创建钱包
    String serialNo = BsinSnowflake.getId();
    wallet.setSerialNo(serialNo);
    // 正常
    wallet.setStatus(WalletStatus.NORMAL.getCode());
    wallet.setWalletName(loginUser.getUsername() + WalletTag.DEPOSIT.getDesc() + "钱包");
    wallet.setWalletTag(WalletTag.DEPOSIT.getCode());
    wallet.setCreateBy(loginUser.getUserId());
    wallet.setCreateTime(new Date());
    
    log.info("钱包基础信息已设置 - 钱包编号: {}, 钱包名称: {}, 钱包类型: {}, 状态: {}", 
        serialNo, wallet.getWalletName(), wallet.getType(), wallet.getStatus());
    walletMapper.insert(wallet);
    log.info("钱包记录已插入数据库，钱包编号: {}", serialNo);

    List<ChainCoin> chainCoinList = new ArrayList<>();
    // 默认EVM钱包
    if( WalletType.DEFAULT.getCode().equals(wallet.getType())){
      log.info("创建默认EVM钱包，租户ID: {}, 角色类型: {}, 角色编号: {}", 
          wallet.getTenantId(), wallet.getBizRoleType(), wallet.getBizRoleTypeNo());
      
      BizRoleTypeChainCoin customerChainCoin = new BizRoleTypeChainCoin();
      customerChainCoin.setTenantId(wallet.getTenantId());
      if(!BizRoleType.MERCHANT.getCode().equals(wallet.getBizRoleType())){
        customerChainCoin.setCreateRoleAccountFlag(1);
      }else {
        customerChainCoin.setCreateCustomerAccountFlag(0);
      }
      chainCoinList = customerChainCoinMapper.selectChainCoinList(customerChainCoin);
      log.info("查询到支持的币种数量: {}", chainCoinList != null ? chainCoinList.size() : 0);
    }else if( WalletType.CUSTOMIZE.getCode().equals(wallet.getType())){
      log.info("创建自定义钱包，环境: {}", wallet.getEnv());
      // 自定义钱包
      if(wallet.getEnv().equals("EVM")){
        // TODO EVM 对应的本币
        log.warn("自定义EVM钱包创建逻辑待实现");
      }
    }

    // 3、 根据租户或商户支持的币种创建钱包地址，并创建钱包账户
    if(chainCoinList != null && !chainCoinList.isEmpty()){
      log.info("开始为钱包 {} 创建 {} 个币种账户", serialNo, chainCoinList.size());
      int accountCount = 0;
      for(ChainCoin chainCoin : chainCoinList){
        try {
          walletAccountBiz.createWalletAccount(wallet, chainCoin);
          accountCount++;
          log.debug("成功创建钱包账户，币种编号: {}, 币种名称: {}", 
              chainCoin.getSerialNo(), chainCoin.getChainCoinName());
        } catch (Exception e) {
          log.error("创建钱包账户失败，币种编号: {}, 错误信息: {}", 
              chainCoin.getSerialNo(), e.getMessage(), e);
          throw e;
        }
      }
      log.info("钱包账户创建完成，成功创建 {}/{} 个账户", accountCount, chainCoinList.size());
    } else {
      log.warn("未找到支持的币种，跳过钱包账户创建");
    }
    
    log.info("钱包创建成功，钱包编号: {}, 钱包名称: {}", wallet.getSerialNo(), wallet.getWalletName());
    return wallet;
  }

  @Override
  public void withdraw(WalletDTO walletDTO) {
    throw new UnsupportedOperationException("withdraw 功能尚未实现");
  }

  @Override
  @ApiDoc(desc = "getPageList")
  @ShenyuDubboClient("/getPageList")
  public Page<WalletVO> getPageList(@Validated(QueryGroup.class) WalletDTO walletDTO) {
    log.debug("请求WalletService.pageList,参数:{}", walletDTO);
    LoginUser loginUser = LoginInfoContextHelper.getLoginUser();
    Pagination pagination = walletDTO.getPagination();
    walletDTO.setTenantId(loginUser.getTenantId());

    // 获取钱包分页数据
    Page<WalletVO> page = walletMapper.pageList(new Page<>(pagination.getPageNum(),pagination.getPageSize()),walletDTO);

    // 在业务层获取平台信息，避免跨数据库查询
    if (page != null && page.getRecords() != null && !page.getRecords().isEmpty()) {
      try {
        // 获取当前租户的平台信息
        Map<String, Object> platformRequestMap = new HashMap<>();
        platformRequestMap.put("tenantId", loginUser.getTenantId());
        Platform platform = platformService.getEcologicalValueAllocationModel(platformRequestMap);

        // 为每个钱包设置平台名称
        String platformName = platform != null ? platform.getPlatformName() : null;
        for (WalletVO walletVO : page.getRecords()) {
          walletVO.setPlatformName(platformName);
        }
      } catch (Exception e) {
        log.warn("获取平台信息失败，tenantId: {}, error: {}", loginUser.getTenantId(), e.getMessage());
        // 平台信息获取失败不影响钱包列表查询
      }
    }

    return page;
  }

  @Override
  @ApiDoc(desc = "edit")
  @ShenyuDubboClient("/edit")
  @Transactional(rollbackFor = Exception.class)
  public void edit(WalletDTO walletDTO) {
    log.info("请求WalletService.editWallet,参数:{}", walletDTO);
    LoginUser loginUser = LoginInfoContextHelper.getLoginUser();
    Wallet updateWallet = new Wallet();
    try{
      updateWallet.setSerialNo(walletDTO.getSerialNo());
      updateWallet.setWalletName(walletDTO.getWalletName());
      updateWallet.setStatus(walletDTO.getStatus());
      updateWallet.setWalletTag(walletDTO.getWalletTag());
      updateWallet.setRemark(walletDTO.getRemark());
      updateWallet.setOutUserId(walletDTO.getOutUserId());
      updateWallet.setUpdateTime(new Date());
      updateWallet.setUpdateBy(loginUser.getUserId());
      walletMapper.updateById(updateWallet);
      log.info("钱包编辑成功，钱包编号: {}", walletDTO.getSerialNo());
    }catch (BusinessException be){
      throw be;
    }catch (Exception e){
      log.error("编辑钱包失败，钱包编号: {}, 错误: {}", 
          walletDTO.getSerialNo(), e.getMessage(), e);
      throw new BusinessException("SYSTEM_ERROR");
    }
  }

  @Override
  @ApiDoc(desc = "delete")
  @ShenyuDubboClient("/delete")
  @Transactional(rollbackFor = Exception.class)
  public void delete(WalletDTO walletDTO) {
    log.info("请求WalletService.delWallet,参数:{}", walletDTO);
    LoginUser user = LoginInfoContextHelper.getLoginUser();
    try{
      Wallet wallet = walletMapper.selectById(walletDTO.getSerialNo());
      if(wallet == null){
        throw new BusinessException("WALLET_NOT_EXIST");
      }
      wallet.setUpdateTime(new Date());
      wallet.setUpdateBy(user.getUserId());
      walletMapper.updateDelFlag(wallet);
      log.info("钱包删除成功，钱包编号: {}", walletDTO.getSerialNo());
    }catch (BusinessException be){
      throw be;
    }catch (Exception e){
      log.error("删除钱包失败，钱包编号: {}, 错误: {}", 
          walletDTO.getSerialNo(), e.getMessage(), e);
      throw new BusinessException("SYSTEM_ERROR");
    }
  }

  /**
   * 返回钱包及钱包下的账户集合
   * @param Wallet
   * @return
   */
  @Override
  @ApiDoc(desc = "getDetail")
  @ShenyuDubboClient("/getDetail")
  public WalletVO getDetail(Wallet walletReq){
    Wallet wallet = walletMapper.selectById(walletReq.getSerialNo());
    WalletVO walletVO = new WalletVO();
    BeanUtils.copyProperties(wallet, walletVO);

    return walletVO;
  }

  @Override
  @ApiDoc(desc = "getDefaultWallet")
  @ShenyuDubboClient("/getDefaultWallet")
  public WalletVO getDefaultWallet(Wallet walletReq){
    LoginUser loginUser = LoginInfoContextHelper.getLoginUser();
    LambdaQueryWrapper<Wallet> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(Wallet::getTenantId, loginUser.getTenantId())
            .eq(Wallet::getBizRoleType, loginUser.getBizRoleType())
            .eq(Wallet::getBizRoleTypeNo, loginUser.getBizRoleTypeNo())
            .eq(Wallet::getType, WalletType.DEFAULT.getCode())
            .eq(Wallet::getDelFlag, 0);
    Wallet wallet = walletMapper.selectOne(queryWrapper);
    if (wallet == null) {
      return null;
    }
    WalletVO walletVO = new WalletVO();
    BeanUtils.copyProperties(wallet, walletVO);
    walletVO.setSerialNo(wallet.getSerialNo());
    // 查询钱包账户
    LambdaQueryWrapper<WalletAccount> walletAccountQueryWrapper = new LambdaQueryWrapper<>();
    walletAccountQueryWrapper.eq(WalletAccount::getWalletNo, wallet.getSerialNo());
    List<WalletAccount> walletAccounts = walletAccountMapper.selectList(walletAccountQueryWrapper);
    walletVO.setWalletAccounts(walletAccounts);
    return walletVO;
  }

  @Override
  public WalletAccount getWalletAccountByChainIdentifier(String chainIdentifier, String bizRoleTypeNo) {
    String nativeCoin = bsinBlockChainEngineFactory.getChainNativeCoin(chainIdentifier);
    log.info("准备用链上资料，链标识:{}，链本币:{}", chainIdentifier, nativeCoin);
    // 根据 chainIdentifier 查询币种
    ChainCoin chainNativeCoin = chainCoinMapper.selectOne(new LambdaQueryWrapper<ChainCoin>()
            .eq(ChainCoin::getChainIdentifier, chainIdentifier)
            .eq(ChainCoin::getCoin, nativeCoin));
    log.debug("链本币查询结果: {}", chainNativeCoin);

    // 查询钱包
    LambdaQueryWrapper<Wallet> walletQueryWrapper = new LambdaQueryWrapper<>();
    walletQueryWrapper.eq(Wallet::getBizRoleTypeNo, bizRoleTypeNo);
    Wallet wallet = walletMapper.selectOne(walletQueryWrapper);
    log.debug("查询业务角色:{} 对应钱包: {}", bizRoleTypeNo, wallet);

    LambdaQueryWrapper<WalletAccount> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(WalletAccount::getWalletNo, wallet.getSerialNo());
    queryWrapper.eq(WalletAccount::getChainCoinNo, chainNativeCoin.getSerialNo());
    WalletAccount walletAccount = walletAccountMapper.selectOne(queryWrapper);
    log.debug("链本币账户: {}", walletAccount);
    return walletAccount;
  }

  /**
   * 获取默认钱包（如果存在）
   * @param tenantId 租户ID
   * @param bizRoleType 业务角色类型
   * @param bizRoleTypeNo 业务角色编号
   * @return 存在则返回钱包对象，否则返回null
   */
  private Wallet getDefaultWalletIfExists(String tenantId, String bizRoleType, String bizRoleTypeNo) {
    LambdaQueryWrapper<Wallet> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(Wallet::getTenantId, tenantId)
               .eq(Wallet::getBizRoleType, bizRoleType)
               .eq(Wallet::getBizRoleTypeNo, bizRoleTypeNo)
               .eq(Wallet::getType, WalletType.DEFAULT.getCode())
               .eq(Wallet::getDelFlag, 0); // 未删除
    return walletMapper.selectOne(queryWrapper);
  }



}
